#ifndef MDTX_USE_RCPPARMADILLO
#define MDTX_USE_RCPPARMADILLO
#endif

#include <RcppArmadillo.h>
using namespace Rcpp;

#include "mdtx-online/updater/updater_type_misc.h"
#include "mdtx-online/indicator/indicator_type_misc.h"
using namespace mdtx::online;

#include "indi_common.h"

using ad_ = indi1<indicator::ad>;

//' Accumulation/Distribution line
//'
//' @param high Numeric vector High
//' @param low Numeric vector Low
//' @param close Numeric vector Close
//' @param volume Numeric vector Volume
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector ad(NumericVector high, NumericVector low, NumericVector close, NumericVector volume)
{
    R_xlen_t n1 = high.length(), n2 = low.length(), n3 = close.length(), n4 = volume.length();
    if (!(n1 == n2 && n2 == n3 && n3 == n4))
    {
        stop("Input lengths do not match.");
    }
    auto c = ad_::create_t();
    return ad_::run_t(c, high, low, close, volume);
}

// [[Rcpp::export(rng = false)]]
SEXP ad_new()
{
    return ad_::create();
}

// [[Rcpp::export(rng = false)]]
NumericVector ad_run(SEXP ptr, NumericVector high, NumericVector low, NumericVector close, NumericVector volume)
{
    return ad_::run(ptr, high, low, close, volume);
}

using adosc_ = indi1<indicator::adosc>;

//' Accumulation/Distribution oscillator
//'
//' @param high Numeric vector High
//' @param low Numeric vector Low
//' @param close Numeric vector Close
//' @param volume Numeric vector Volume
//' @param fast_period Fast period
//' @param slow_period Slow period
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector adosc(NumericVector high, NumericVector low, NumericVector close, NumericVector volume, int fast_period, int slow_period)
{
    R_xlen_t n1 = high.length(), n2 = low.length(), n3 = close.length(), n4 = volume.length();
    if (!(n1 == n2 && n2 == n3 && n3 == n4))
    {
        stop("Input lengths do not match.");
    }
    auto c = adosc_::create_t(fast_period, slow_period);
    return adosc_::run_t(c, high, low, close, volume);
}

// [[Rcpp::export(rng = false)]]
SEXP adosc_new(int fast_period, int slow_period)
{
    return adosc_::create(fast_period, slow_period);
}

// [[Rcpp::export(rng = false)]]
NumericVector adosc_run(SEXP ptr, NumericVector high, NumericVector low, NumericVector close, NumericVector volume)
{
    return adosc_::run(ptr, high, low, close, volume);
}


using atr_ = indi1<indicator::atr>;

//' Average true range
//'
//' @param high Numeric vector High
//' @param low Numeric vector Low
//' @param close Numeric vector Close
//' @param period Period of rolling window
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector atr(NumericVector high, NumericVector low, NumericVector close, int period)
{
    R_xlen_t n1 = high.length(), n2 = low.length(), n3 = close.length();
    if (!(n1 == n2 && n2 == n3))
    {
        stop("Input lengths do not match.");
    }
    auto c = atr_::create_t(period);
    return atr_::run_t(c, high, low, close);
}

// [[Rcpp::export(rng = false)]]
SEXP atr_new(int period)
{
    return atr_::create(period);
}

// [[Rcpp::export(rng = false)]]
NumericVector atr_run(SEXP ptr, NumericVector high, NumericVector low, NumericVector close)
{
    return atr_::run(ptr, high, low, close);
}

using bop_ = indi1<indicator::bop>;

//' Balance of power
//'
//' @param open Numeric vector Open
//' @param high Numeric vector High
//' @param low Numeric vector Low
//' @param close Numeric vector Close
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector bop(NumericVector open, NumericVector high, NumericVector low, NumericVector close)
{
    R_xlen_t n1 = open.length(), n2 = high.length(), n3 = low.length(), n4 = close.length();
    if (!(n1 == n2 && n2 == n3 && n3 == n4))
    {
        stop("Input lengths do not match.");
    }
    auto c = bop_::create_t();
    return bop_::run_t(c, open, high, low, close);
}

// [[Rcpp::export(rng = false)]]
SEXP bop_new()
{
    return bop_::create();
}

// [[Rcpp::export(rng = false)]]
NumericVector bop_run(SEXP ptr, NumericVector open, NumericVector high, NumericVector low, NumericVector close)
{
    return bop_::run(ptr, open, high, low, close);
}

using cmo_ = indi1<indicator::cmo>;

//' Chande momentum oscillator
//'
//' @param x Numeric vector
//' @param period Period of rolling window
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector cmo(NumericVector x, int period)
{
    auto c = cmo_::create_t(period);
    return cmo_::run_t(c, x);
}

// [[Rcpp::export(rng = false)]]
SEXP cmo_new(int period)
{
    return cmo_::create(period);
}

// [[Rcpp::export(rng = false)]]
NumericVector cmo_run(SEXP ptr, NumericVector x)
{
    return cmo_::run(ptr, x);
}

using cvi_ = indi1<indicator::cvi>;

//' Chaikins volatility
//'
//' @param high Numeric vector High
//' @param low Numeric vector Low
//' @param period Period of rolling window
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector cvi(NumericVector high, NumericVector low, int period)
{
    R_xlen_t n1 = high.length(), n2 = low.length();
    if (!(n1 == n2))
    {
        stop("Input lengths do not match.");
    }
    auto c = cvi_::create_t(period);
    return cvi_::run_t(c, high, low);
}

// [[Rcpp::export(rng = false)]]
SEXP cvi_new(int period)
{
    return cvi_::create(period);
}

// [[Rcpp::export(rng = false)]]
NumericVector cvi_run(SEXP ptr, NumericVector high, NumericVector low)
{
    return cvi_::run(ptr, high, low);
}

class decay_exp_
{
private:
    updater::expdecay_updater decay;

public:
    explicit decay_exp_(double rate)
        : decay(rate)
    {
    }

    NumericVector run(LogicalVector x)
    {
        NumericVector ans(x.length());
        for (R_xlen_t i = 0; i < x.length(); ++i)
        {
            decay.insert(x[i]);
            ans[i] = decay.decay;
        }
        return ans;
    }
};

//' Exponential decay
//'
//' @param x Logical vector Signal
//' @param rate Rate of decay
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector decay_exp(LogicalVector x, double rate)
{
    decay_exp_ c(rate);
    return c.run(x);
}

// [[Rcpp::export(rng = false)]]
SEXP decay_exp_new(double rate)
{
    decay_exp_ *ptr = new decay_exp_(rate);
    return XPtr<decay_exp_>(ptr);
}

// [[Rcpp::export(rng = false)]]
NumericVector decay_exp_run(SEXP ptr, LogicalVector x)
{
    XPtr<decay_exp_> c(ptr);
    return c->run(x);
}

class decay_linear_
{
private:
    updater::lindecay_updater decay;

public:
    explicit decay_linear_(double rate)
        : decay(rate)
    {
    }

    NumericVector run(LogicalVector x)
    {
        NumericVector ans(x.length());
        for (R_xlen_t i = 0; i < x.length(); ++i)
        {
            decay.insert(x[i]);
            ans[i] = decay.decay;
        }
        return ans;
    }
};

//' Linear decay
//'
//' @param x Logical vector Signal
//' @param rate Rate of decay
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector decay_linear(LogicalVector x, double rate)
{
    decay_linear_ c(rate);
    return c.run(x);
}

// [[Rcpp::export(rng = false)]]
SEXP decay_linear_new(double rate)
{
    decay_linear_ *ptr = new decay_linear_(rate);
    return XPtr<decay_linear_>(ptr);
}

// [[Rcpp::export(rng = false)]]
NumericVector decay_linear_run(SEXP ptr, LogicalVector x)
{
    XPtr<decay_linear_> c(ptr);
    return c->run(x);
}

using dpo_ = indi1<indicator::dpo>;

//' Detrended price oscillator
//'
//' @param x Numeric vector
//' @param period Period of rolling window
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector dpo(NumericVector x, int period)
{
    auto c = dpo_::create_t(period);
    return dpo_::run_t(c, x);
}

// [[Rcpp::export(rng = false)]]
SEXP dpo_new(int period)
{
    return dpo_::create(period);
}

// [[Rcpp::export(rng = false)]]
NumericVector dpo_run(SEXP ptr, NumericVector x)
{
    return dpo_::run(ptr, x);
}

using emv_ = indi1<indicator::emv>;

//' Ease of movement
//'
//' This is the only indicator that I really hate because the unit doesn't make any sense at all. Good luck with it.
//'
//' @param high Numeric vector High
//' @param low Numeric vector Low
//' @param volume Numeric vector Volume
//' @param volume_scale scaling factor to make volume close to price level, usually 0.0001
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector emv(NumericVector high, NumericVector low, NumericVector volume, double volume_scale = 0.0001)
{
    R_xlen_t n1 = high.length(), n2 = low.length(), n3 = volume.length();
    if (!(n1 == n2 && n2 == n3))
    {
        stop("Input lengths do not match.");
    }
    auto c = emv_::create_t(volume_scale);
    return emv_::run_t(c, high, low, volume);
}

// [[Rcpp::export(rng = false)]]
SEXP emv_new(double volume_scale = 0.0001)
{
    return emv_::create(volume_scale);
}

// [[Rcpp::export(rng = false)]]
NumericVector emv_run(SEXP ptr, NumericVector high, NumericVector low, NumericVector volume)
{
    return emv_::run(ptr, high, low, volume);
}

using mass_ = indi1<indicator::mass>;

//' Mass index
//'
//' @param x Numeric vector
//' @param period Period of rolling window
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector mass(NumericVector x, int period)
{
    auto c = mass_::create_t(period);
    return mass_::run_t(c, x);
}

// [[Rcpp::export(rng = false)]]
SEXP mass_new(int period)
{
    return mass_::create(period);
}

// [[Rcpp::export(rng = false)]]
NumericVector mass_run(SEXP ptr, NumericVector x)
{
    return mass_::run(ptr, x);
}

using mfi_ = indi1<indicator::mfi>;

//' Money flow index
//'
//' @param close Numeric vector Close
//' @param turnover Numeric vector Turnover
//' @param period Period of rolling window
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector mfi(NumericVector close, NumericVector turnover, int period)
{
    R_xlen_t n1 = close.length(), n2 = turnover.length();
    if (!(n1 == n2))
    {
        stop("Input lengths do not match.");
    }
    auto c = mfi_::create_t(period);
    return mfi_::run_t(c, close, turnover);
}

// [[Rcpp::export(rng = false)]]
SEXP mfi_new(int period)
{
    return mfi_::create(period);
}

// [[Rcpp::export(rng = false)]]
NumericVector mfi_run(SEXP ptr, NumericVector close, NumericVector turnover)
{
    return mfi_::run(ptr, close, turnover);
}

using obv_ = indi1<indicator::obv>;

//' On balance volume
//'
//' @param price Numeric vector Price
//' @param volume Numeric vector Volume
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector obv(NumericVector price, NumericVector volume)
{
    R_xlen_t n1 = price.length(), n2 = volume.length();
    if (!(n1 == n2))
    {
        stop("Input lengths do not match.");
    }
    auto c = obv_::create_t();
    return obv_::run_t(c, price, volume);
}

// [[Rcpp::export(rng = false)]]
SEXP obv_new()
{
    return obv_::create();
}

// [[Rcpp::export(rng = false)]]
NumericVector obv_run(SEXP ptr, NumericVector price, NumericVector volume)
{
    return obv_::run(ptr, price, volume);
}

using psar_ = indi2<indicator::psar>;

//' Parabolic SAR
//'
//' @param high Numeric vector High
//' @param low Numeric vector Low
//' @param acc_init acc factor initial value, usually 0.02
//' @param acc_max acc factor max value, usually 0.2
//' @return List of PSAR and trend
//' @export
// [[Rcpp::export]]
List psar(NumericVector high, NumericVector low, double acc_init = 0.02, double acc_max = 0.2)
{
    R_xlen_t n1 = high.length(), n2 = low.length();
    if (!(n1 == n2))
    {
        stop("Input lengths do not match.");
    }
    auto c = psar_::create_t(acc_init, acc_max);
    auto ans = psar_::run_t(c, high, low);
    ans.names() = CharacterVector::create("PSAR", "trend");
    return ans;
}

// [[Rcpp::export]]
SEXP psar_new(double acc_init = 0.02, double acc_max = 0.2)
{
    return psar_::create(acc_init, acc_max);
}

// [[Rcpp::export]]
List psar_run(SEXP ptr, NumericVector high, NumericVector low)
{
    auto ans = psar_::run(ptr, high, low);
    ans.names() = CharacterVector::create("PSAR", "trend");
    return ans;
}

using rollsum_ = indi1<indicator::rollsum>;

//' Rolling sum
//'
//' @param x Numeric vector to sum over
//' @param period Period of rolling window
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector rollsum(NumericVector x, int period)
{
    auto c = rollsum_::create_t(period);
    return rollsum_::run_t(c, x);
}

// [[Rcpp::export(rng = false)]]
SEXP rollsum_new(int period)
{
    return rollsum_::create(period);
}

// [[Rcpp::export(rng = false)]]
NumericVector rollsum_run(SEXP ptr, NumericVector x)
{
    return rollsum_::run(ptr, x);
}

using stosc_ = indi1<indicator::stosc>;

//' Stochastic oscillator
//'
//' @param high Numeric vector High
//' @param low Numeric vector Low
//' @param close Numeric vector Close
//' @param period Period of rolling window
//' @return Numeric vector
//' @export
// [[Rcpp::export]]
NumericVector stosc(NumericVector high, NumericVector low, NumericVector close, int period)
{
    R_xlen_t n1 = high.length(), n2 = low.length(), n3 = close.length();
    if (!(n1 == n2 && n2 == n3))
    {
        stop("Input lengths do not match.");
    }
    auto c = stosc_::create_t(period);
    return stosc_::run_t(c, high, low, close);
}

using vhf_ = indi1<indicator::vhf>;

//' Vertical horizontal filter
//'
//' @param x Numeric vector
//' @param period Period of rolling window
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector vhf(NumericVector x, int period)
{
    auto c = vhf_::create_t(period);
    return vhf_::run_t(c, x);
}

// [[Rcpp::export(rng = false)]]
SEXP vhf_new(int period)
{
    return vhf_::create(period);
}

// [[Rcpp::export(rng = false)]]
NumericVector vhf_run(SEXP ptr, NumericVector x)
{
    return vhf_::run(ptr, x);
}

using volidx_ = indi2<indicator::volidx>;

//' Positive/Negative volume index
//'
//' @param price Numeric vector Price
//' @param volume Numeric vector Volume
//' @param initialise init PVI/NVI value
//' @return List of PVI and NVI
//' @export
// [[Rcpp::export]]
List volidx(NumericVector price, NumericVector volume, double initialise = 1000.0)
{
    R_xlen_t n1 = price.length(), n2 = volume.length();
    if (!(n1 == n2))
    {
        stop("Input lengths do not match.");
    }
    auto c = volidx_::create_t(initialise);
    auto ans = volidx_::run_t(c, price, volume);
    ans.names() = CharacterVector::create("PVI", "NVI");
    return ans;
}

// [[Rcpp::export(rng = false)]]
SEXP volidx_new(double initialise = 1000.0)
{
    return volidx_::create(initialise);
}

// [[Rcpp::export(rng = false)]]
List volidx_run(SEXP ptr, NumericVector price, NumericVector volume)
{
    auto ans = volidx_::run(ptr, price, volume);
    ans.names() = CharacterVector::create("PVI", "NVI");
    return ans;
}

using wad_ = indi1<indicator::wad>;

//' Williams Accumulation/Distribution
//'
//' @param high Numeric vector High
//' @param low Numeric vector Low
//' @param close Numeric vector Close
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector wad(NumericVector high, NumericVector low, NumericVector close)
{
    R_xlen_t n1 = high.length(), n2 = low.length(), n3 = close.length();
    if (!(n1 == n2 && n2 == n3))
    {
        stop("Input lengths do not match.");
    }
    auto c = wad_::create_t();
    return wad_::run_t(c, high, low, close);
}

SEXP wad_new()
{
    return wad_::create();
}

NumericVector wad_run(SEXP ptr, NumericVector high, NumericVector low, NumericVector close)
{
    return wad_::run(ptr, high, low, close);
}

using willr_ = indi1<indicator::willr>;

//' William's \%R
//'
//' @param high Numeric vector High
//' @param low Numeric vector Low
//' @param close Numeric vector Close
//' @param period Period of rolling window
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector willr(NumericVector high, NumericVector low, NumericVector close, int period)
{
    R_xlen_t n1 = high.length(), n2 = low.length(), n3 = close.length();
    if (!(n1 == n2 && n2 == n3))
    {
        stop("Input lengths do not match.");
    }
    auto c = willr_::create_t(period);
    return willr_::run_t(c, high, low, close);
}

SEXP willr_new(int period)
{
    return willr_::create(period);
}

NumericVector willr_run(SEXP ptr, NumericVector high, NumericVector low, NumericVector close)
{
    return willr_::run(ptr, high, low, close);
}
